require 'rake'
require 'erb'

################################################################################
# Prepare
####
ROOT = File.expand_path(File.dirname(__FILE__))
LIBS = File.join(ROOT, 'lib')
SRCS = File.join(ROOT, 'src')

INCS = {
  'type'   => [],
  'crypto' => ['type'],
  'cookie' => ['type'],
  'query'  => ['type']
};
DIRS = INCS.keys.sort

################################################################################
# Versioning
####
PREC = nil # default = 2
TIME = Time.now.utc
VERS = File.join(ROOT, 'VERSION')
$ver = nil

def setserial(vers)
  $ver = vers
end

def serial
  if ($ver.nil?)
    p = (PREC.is_a?(Numeric) && PREC > 0) ? PREC : 2
    f = "%.#{p*2}f"
    s = "0.%m#{'%d'if(p>1)}#{'%H'if(p>2)}#{'%M'if(p>3)}#{'%S'if(p>4)}"
    setserial(sprintf(f, (TIME.strftime("%Y").to_f - 2000 + TIME.strftime(s).to_f)))
  end
  $ver
end

################################################################################
# Tools
####
module Builder
  module Utilities
    JSMIN = File.join(ROOT, 'jsmin.rb')
    
    def jsmin(*filenames)
      filenames.map {|name| File.expand_path(name)}
      filenames.map {|name| (`ruby #{JSMIN} < #{name}`).strip}.join($/)
    end
    
    def include(*filenames)
      filenames.map {|name| Reader.new(name, nil).to_s.strip}.join($/+$/)
    end
  end
  
  class Reader
    include Utilities
    
    def initialize(filename, anchor)
      @filename = File.expand_path(filename)
      @template = ERB.new(IO.read(@filename), nil, '%')
      @anchor = anchor === true ? true : false
    end
    
    def to_s
      @template.result(binding).gsub(/[ \t]+$/m, '').strip + (@anchor ? $/ : '')
    end
  end
end

module Sources
  DEFL = 'cgus'
  LDIR = [].concat(DIRS).concat([DEFL]).max{|x,y| x.length <=> y.length}.length
  PCKG = {}
  
  def Sources.define(name, anchor)
    if (PCKG[name].nil?)
      PCKG[name] = anchor
      return true
    end
  end
  
  def Sources.format(file)
    keys = { 'src' => '++', 'lib' => '=>'}
    file.sub(/#{Regexp.quote(ROOT)}\/([^\/]+)\/(?:([^\/]+)\/)?([^\/]+)/) do
      [($2 || DEFL).ljust(LDIR), keys[$1], $3].join(' ')
    end
  end
  
  def Sources.check(file)
    print ' ' + (File.exists?(file) ? '+' : '-')
    print '  ' + Sources.format(file) + $/
  end
  
  def Sources.clear(name, inc)
    return nil if (PCKG[name].nil?)
    return nil if (!inc.nil? && PCKG[inc].nil?)
    remove = inc.nil? ?
      File.join(LIBS, "#{name}.js") :
      File.join(File.split(PCKG[inc])[0], "inc.#{name}.js")
    Sources.check(remove) if (File.exists?(remove) && File.delete(remove) > 0)
  end
  
  def Sources.build(name, inc)
    return nil if (PCKG[name].nil?)
    return nil if (!inc.nil? && PCKG[inc].nil?)
    pckg = File.split(PCKG[name])
    create = inc.nil? ?
      File.join(LIBS, "#{name}.js") :
      File.join(File.split(PCKG[inc])[0], "inc.#{name}.js")
    Dir.chdir(pckg[0]) do
      File.open(create, 'w+') do |out|
        out << Builder::Reader.new(pckg[1], true)
      end
    end
    Sources.check(create) if (File.exists?(create))
  end
end

################################################################################
# Setup
####
DIRS.each do |dir|
  Sources.define(dir, File.join(SRCS, dir, 'anchor.js'))
end

################################################################################
# Tasks
####
task :default do
  print [
    "Tasks:",
    "  build (all)",
    "  clean (rm)",
    "  status (st)",
    "",
    "Versioning Tasks:",
    "  cache",
    "  clear"
  ].join($/) + $/
end

task :clear do
  print $/ + "== Clear" + $/
  print " -  cache" + $/
  File.open(VERS, 'w+') do |v|
    v << ""
  end
end

task :cache do
  print $/ + "== Cache" + $/
  print " +  cache (" + serial.to_s + ")" + $/
  File.open(VERS, 'w+') do |v|
    v << serial.to_s + $/
  end
end

task :all => :build
task :rm  => :clean
task :st  => :status

desc "Build all library scripts"
task :build do
  if (File.exists?(VERS))
    vers = IO.readlines(VERS).to_s.strip
    setserial(vers) if !(vers =~ /^[0-9]+\.(?:[0-9]{2}){1,5}$/).nil?
  end
  
  print $/ + '== Build' + ' :: ' + TIME.strftime('%Y-%m-%d %I:%M:%S') + ' :: ' + serial.to_s + $/
  INCS.keys.sort.each do |util|
    INCS[util].sort.each do |inc|
      Sources.build(inc, util)
    end
  end
  INCS.keys.sort.each do |util|
    Sources.build(util, nil)
  end
  Rake::Task['clear'].reenable
end

desc "Delete all library scripts"
task :clean do
  print $/ + '== Clean' + $/
  INCS.keys.sort.each do |util|
    Sources.clear(util, nil)
  end
  INCS.keys.sort.each do |util|
    INCS[util].sort.each do |inc|
      Sources.clear(inc, util)
    end
  end
end

desc "Check build status of library scrips"
task :status do
  print $/ + '== Status' + $/
  INCS.keys.sort.each do |util|
    INCS[util].sort.each do |inc|
      Sources.check(File.join(SRCS, util, "inc.#{inc}.js"))
    end
  end
  INCS.keys.sort.each do |util|
    Sources.check(File.join(LIBS, "#{util}.js"))
  end
end

module Rake
  class Task
    def reenable
      @already_invoked = false
    end
  end
end
